package net.jkdmanager.common.services.plugins;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import javax.annotation.PostConstruct;

import net.jkdmanager.common.api.model.plugins.PluginDescriptor;
import net.jkdmanager.common.api.model.plugins.PluginPropertyDescriptor;
import net.jkdmanager.common.api.model.plugins.PluginsDescriptor;
import net.jkdmanager.common.api.plugins.DirectorI;
import net.jkdmanager.common.api.plugins.DistributorI;
import net.jkdmanager.common.api.plugins.PluginI;
import net.jkdmanager.common.api.plugins.RunnerI;
import net.jkdmanager.common.api.plugins.annotations.Plugin;
import net.jkdmanager.common.api.plugins.annotations.PluginProperty;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class PluginRegistryServiceImpl implements PluginRegistryService {
	
	private static final Log log = LogFactory.getLog(PluginRegistryServiceImpl.class);
	
	@Autowired
	private List<PluginI> plugins;
	
	private Map<String, PluginI> pluginsMap = new ConcurrentHashMap<String, PluginI>();
	
	private PluginsDescriptor pluginsDescriptor = new PluginsDescriptor();
	
	@PostConstruct
	protected void init() {
		
		for (PluginI plugin : plugins) {
			Plugin pluginAnnotation = plugin.getClass().getAnnotation(Plugin.class);
			if (pluginAnnotation == null) {
				log.error("Cannot register plugin " + plugin.getClass().getName() + ". It doesn't have annotation Plugin!");
			} else {
				if (pluginsMap.containsKey(pluginAnnotation.key())) {
					log.error("Cannot register plugin " + plugin.getClass().getName() + " under key " + pluginAnnotation.key() + ". This key is already registered.");
				} else {
					pluginsMap.put(pluginAnnotation.key(), plugin);
					//analyze plugin 
					PluginDescriptor pluginDescriptor = createPluginDescriptor(pluginAnnotation);
					if (plugin instanceof RunnerI) {
						pluginsDescriptor.getRunners().add(pluginDescriptor);
					} else if (plugin instanceof DirectorI) {
						pluginsDescriptor.getDirectors().add(pluginDescriptor);
					} else if (plugin instanceof DistributorI) {
						pluginsDescriptor.getDistributors().add(pluginDescriptor);
					} else {
						log.error("Plugin is of unknown type! Class: " + plugin.getClass().getName() + ", key: " + pluginAnnotation.key());
					}
				}
			}
		}
						
	}
	
	private PluginDescriptor createPluginDescriptor(Plugin pluginAnn) {
		
		PluginDescriptor pluginDescriptor = new PluginDescriptor();
		pluginDescriptor.setKey(pluginAnn.key());
		pluginDescriptor.setTitle(pluginAnn.title());
		pluginDescriptor.setProperties(new ArrayList<PluginPropertyDescriptor>());
		
		for (PluginProperty pluginPropertyAnn : pluginAnn.properties()) {
			PluginPropertyDescriptor pluginPropertyDescriptor = new PluginPropertyDescriptor();
			pluginPropertyDescriptor.setKey(pluginPropertyAnn.key());
			pluginDescriptor.getProperties().add(pluginPropertyDescriptor);
		}
		
		return pluginDescriptor;
		
	}
	
	public PluginsDescriptor getPluginsDescriptor() {
		return pluginsDescriptor;
	}
	
	public List<PluginI> getAllPlugins() {
		return plugins;
	}
		
	@SuppressWarnings("unchecked")
	public <T extends PluginI> List<T> getAllPlugins(Class<T> clazz) {
		List<T> selectedPlugins = new ArrayList<T>();
		for (PluginI plugin : pluginsMap.values()) {
			if (plugin.getClass().isInstance(clazz)) {
				selectedPlugins.add((T)plugin);
			}
		}
		return selectedPlugins;
	}
	
	@SuppressWarnings("unchecked")
	public <T extends PluginI> T getPlugin(Class<T> clazz, String pluginKey) {
		return (T)pluginsMap.get(pluginKey);
	}
	
}
